﻿#region File and License Information
/*
<File>
	<License>
		Copyright © 2009 - 2017, Daniel Vaughan. All rights reserved.
		This file is part of Calcium (http://calciumsdk.net), 
		which is released under the MIT License.
		See file /Documentation/License.txt for details.
	</License>
	<CreationDate>2010-03-25 20:37:27Z</CreationDate>
</File>
*/
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Outcoder.ComponentModel;

namespace Outcoder.Services
{
	/// <summary>
	/// Default implementation of the <see cref="IStringParserService"/>.
	/// </summary>
	public class StringParserService : IStringParserService
	{
		readonly Dictionary<string, IConverter> converters = new Dictionary<string, IConverter>();
		readonly ReaderWriterLockSlim convertersLock = new ReaderWriterLockSlim();

		public void RegisterConverter(string tagName, Func<object, object> convertFunc)
		{
			ArgumentValidator.AssertNotNull(tagName, "tagName");
			ArgumentValidator.AssertNotNull(convertFunc, "convertFunc");

			RegisterConverterCore(tagName, new DelegateConverter(convertFunc));
		}

		public void RegisterConverter(string tagName, IConverter converter)
		{
			ArgumentValidator.AssertNotNull(tagName, "tagName");

			RegisterConverterCore(tagName, converter);
		}

		void RegisterConverterCore(string tagName, IConverter converter)
		{
			try
			{
				convertersLock.EnterWriteLock();
				converters[tagName.ToLower()] = converter;
			}
			finally
			{
				convertersLock.ExitWriteLock();
			}
		}

		public string Parse(string text)
		{
			return Parse(text, null);
		}

		public string Parse(string text, IDictionary<string, string> tagValues)
		{
			ArgumentValidator.AssertNotNull(text, "text");

			var sb = new StringBuilder(text.Length);

			for (int i = 0; i < text.Length; i++)
			{
				if (text[i] != '$')
				{
					sb.Append(text[i]);
					continue;
				}

				int start = i;

				if (++i >= text.Length)
				{
					break;
				}

				if (text[i] != '{')
				{
					sb.Append(text[i]);
					continue;
				}

				int end;
				for (end = ++i; end < text.Length; end++)
				{
					if (text[end] == '}')
					{
						break;
					}
				}

				string replacement;
				if (end == text.Length || (replacement = Replace(text.Substring(i, end - i), tagValues)) == null)
				{
					sb.Append(text.Substring(start, end - start));
					break;
				}

				sb.Append(replacement);
				i = end;
			}

			return sb.ToString();
		}

		bool GetConverter(string tagName, out IConverter converter)
		{
			try
			{
				convertersLock.EnterReadLock();
				return converters.TryGetValue(tagName.ToLower(), out converter) && converter != null;
			}
			finally
			{
				convertersLock.ExitReadLock();
			}
		}

		string GetShortDateString(DateTime dateTime)
		{
#if NETFX_CORE
			return dateTime.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortDatePattern);
#else
			return dateTime.ToShortDateString();
#endif
		}

		string Replace(string tagContent, IDictionary<string, string> customTags)
		{
			string result = null;
			string tagContentLower = tagContent.ToLower();
			switch (tagContentLower)
			{
				case "date":
					result = GetShortDateString(DateTime.Today);
					break;
				case "time":
					result = GetShortDateString(DateTime.Now);
					break;
				case "test123":
					result = "true";
					break;
				default:
					string tagName;
					string tagArgs = null;
					int indexOfArg = tagContent.IndexOf(':');
					if (indexOfArg > 0)
					{
						tagName = tagContent.Substring(0, indexOfArg);
						int tagLength = tagContent.Length - (indexOfArg + 1);
						if (tagLength > 0)
						{
							tagArgs = tagContent.Substring(indexOfArg + 1, tagLength);
						}
					}
					else
					{
						tagName = tagContent.ToLower();
					}

					if (customTags != null)
					{
						customTags.TryGetValue(tagName, out result);
					}

					if (result == null)
					{
						IConverter converter;
						if (GetConverter(tagName, out converter))
						{
							object conversionResult = converter.Convert(tagArgs);
							result = conversionResult != null ? conversionResult.ToString() : null;
						}
					}
					break;
			}

			return result;
		}
	}
}
